/**
 * Copyright (c) 2019 Coder League
 * All rights reserved.
 *
 * File：BoundService.java
 * History:
 *         2019年6月5日: Initially created, Chrise.
 */
package club.coderleague.ilsp.service.bind;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;

import club.coderleague.ilsp.common.domain.beans.UserSession;
import club.coderleague.ilsp.common.domain.beans.WebAuthContext;
import club.coderleague.ilsp.common.domain.enums.EntityState;
import club.coderleague.ilsp.common.domain.enums.MemberBindType;
import club.coderleague.ilsp.common.domain.enums.MemberOrigin;
import club.coderleague.ilsp.common.domain.enums.MemberType;
import club.coderleague.ilsp.common.domain.enums.UserGroup;
import club.coderleague.ilsp.common.domain.enums.WebAuthScene;
import club.coderleague.ilsp.common.domain.enums.WebAuthor;
import club.coderleague.ilsp.dao.BigCustomerPersonDao;
import club.coderleague.ilsp.dao.BigCustomersMgrDao;
import club.coderleague.ilsp.dao.MemberBindDao;
import club.coderleague.ilsp.dao.MembersDao;
import club.coderleague.ilsp.dao.MerchantsDao;
import club.coderleague.ilsp.entities.Bigcustomerpersons;
import club.coderleague.ilsp.entities.Memberbinds;
import club.coderleague.ilsp.entities.Members;
import club.coderleague.ilsp.entities.Merchants;
import club.coderleague.ilsp.service.AbstractService;
import club.coderleague.ilsp.service.interfaces.AliyunInterfaceService;
import club.coderleague.ilsp.util.CommonUtil;
import club.coderleague.security.AlgorithmBeanFactory;
import club.coderleague.security.algorithm.MD5Signer;

/**
 * 绑定服务。
 * @author Chrise
 */
@Service
public class BoundService extends AbstractService {
	private static final Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$");
	
	@Autowired
	private MembersDao membersDao;
	@Autowired
	private MemberBindDao memberBindDao;
	@Autowired
	private BigCustomersMgrDao bigCustomersDao;
	@Autowired
	private BigCustomerPersonDao bcPersonDao;
	@Autowired
	private MerchantsDao merchantsDao;
	@Autowired
	private AliyunInterfaceService ayiService;
	private MD5Signer signer;
	
	/**
	 * 初始化。
	 * @author Chrise 2019年6月5日
	 */
	@PostConstruct
	private void initialize() {
		this.signer = AlgorithmBeanFactory.getAlgorithmBean(MD5Signer.class);
	}
	
	/**
	 * 检查开放标识是否已绑定。
	 * @author Chrise 2019年6月5日
	 * @param scene 授权场景。
	 * @param author 授权人。
	 * @param openId 开放标识。
	 * @return 用户会话对象。
	 */
	public UserSession execCheckBound(WebAuthScene scene, WebAuthor author, String openId) {
		// 查询用户会话
		UserSession us = null;
		switch (scene) {
			case SCAN_PAY:
			case MEMBER_MALL:
				us = this.membersDao.queryUserSession(authorToBindType(author), openId);
				if (us != null) this.membersDao.updateLoginTime(us.getUserid());
				break;
			case CUSTOMER_AREA:
				us = this.bigCustomersDao.queryUserSession(openId);
				if (us != null) this.bigCustomersDao.updateLoginTime(us.getUserid());
				break;
			case MERCHANT_CONSOLE:
				us = this.merchantsDao.queryUserSession(openId);
				if (us != null) this.merchantsDao.updateLoginTime(us.getUserid());
				break;
		}
		
		return us;
	}
	
	/**
	 * 绑定手机号。
	 * @author Chrise 2019年6月5日
	 * @param phone 手机号。
	 * @param code 验证码。
	 * @param session 会话对象。
	 * @param model 模型对象。
	 * @return 结果消息。
	 * @throws Exception 绑定异常。
	 */
	public String execBindPhone(String phone, String code, HttpSession session, ModelMap model) throws Exception {
		// 验证参数有效性
		if (!PHONE_PATTERN.matcher(phone).matches()) return "手机号无效";
		if (CommonUtil.isEmpty(code)) return "验证码无效";
		
		// 获取网页授权上下文
		WebAuthContext context = (WebAuthContext)session.getAttribute(WebAuthContext.SESSION_KEY);
		if (context == null) return "授权已失效";
		
		// 检查验证码有效性
		String cached = this.ayiService.getCachedVercode(session);
		if (CommonUtil.isEmpty(cached)) return "验证码已失效";
		if (!code.equals(cached)) return "验证码错误";
		
		// 删除验证码及授权缓存
		this.ayiService.clearCache(session);
		session.removeAttribute(WebAuthContext.SESSION_KEY);
		
		// 绑定手机号
		Object bound = null;
		switch (context.getScene()) {
			case SCAN_PAY:
			case MEMBER_MALL:
				bound = this.bindMember(context.getScene(), context.getAuthor(), phone, context.getOpenid());
				break;
			case CUSTOMER_AREA:
				bound = this.bindPerson(phone, context.getOpenid());
				break;
			case MERCHANT_CONSOLE:
				bound = this.bindMerchant(phone, context.getOpenid());
				break;
		}
		
		if (bound instanceof UserSession) {		// 绑定成功
			session.setAttribute(UserSession.SESSION_KEY, bound);
			model.addAttribute(model.keySet().iterator().next(), true);
			return context.getRedirect();
		}
		
		return (String)bound;
	}
	
	/**
	 * 绑定会员。
	 * @author Chrise 2019年6月5日
	 * @param scene 授权场景。
	 * @param author 授权人。
	 * @param phone 手机号。
	 * @param openId 开放标识。
	 * @return 用户会话对象或错误消息。
	 * @throws Exception 会员绑定异常。
	 */
	private Object bindMember(WebAuthScene scene, WebAuthor author, String phone, String openId) throws Exception {
		Date now = new Date();
		String phoneHash = this.signer.sign(phone);
		MemberBindType mbt = authorToBindType(author);
		
		// 检查开放标识是否已绑定
		String bound = this.membersDao.queryBoundPhone(mbt, openId);
		if (bound != null && !bound.equals(phoneHash)) return "已绑定其他手机号";
		
		Members member = this.membersDao.findByPhonehash(phoneHash);
		if (member == null) {
			// 创建会员
			MemberOrigin mo = (scene == WebAuthScene.MEMBER_MALL) ? MemberOrigin.WEIXIN_SUBSCRIBE : authorToMemberOrigin(author);
			member = new Members(EntityState.VALID.getValue(), mo.getValue(), phone, phoneHash, MemberType.NORMALS.getValue(), null, now);
			this.membersDao.save(member);
			
			// 创建会员绑定
			Memberbinds bind = new Memberbinds(EntityState.VALID.getValue(), member.getEntityid(), mbt.getValue(), openId);
			this.memberBindDao.save(bind);
		} else {
			// 账号已冻结
			if (EntityState.FROZEN.equalsValue(member.getEntitystate())) return "绑定的账号已冻结";
			
			// 清除原有绑定
			List<Memberbinds> binds = this.memberBindDao.queryOtherBinds(member.getEntityid(), mbt.getValue());
			if (!binds.isEmpty()) {
				for (Memberbinds bind : binds) {
					bind.setEntitystate(EntityState.INVALID.getValue());
				}
			}
			
			// 修改最后登录时间
			member.setLastlogin(now);
			
			Memberbinds bind = this.memberBindDao.findByMemberidAndBindtypeAndBindid(member.getEntityid(), mbt.getValue(), openId);
			if (bind == null) {
				// 创建会员绑定
				bind = new Memberbinds(EntityState.VALID.getValue(), member.getEntityid(), mbt.getValue(), openId);
				this.memberBindDao.save(bind);
			} else {
				if (EntityState.INVALID.equalsValue(bind.getEntitystate())) {
					// 修改会员绑定状态
					bind.setEntitystate(EntityState.VALID.getValue());
				}
			}
		}
		
		// 创建用户会话对象
		UserSession us = new UserSession();
		us.setUserid(member.getEntityid());
		us.setUsergroup(UserGroup.MEMBER.getValue());
		us.setMembertype(member.getMembertype());
		us.setNickname(member.getNickname());
		us.setOpenid(openId);
		return us;
	}
	
	/**
	 * 绑定人员。
	 * @author Chrise 2019年6月5日
	 * @param phone 手机号。
	 * @param openId 开放标识。
	 * @return 用户会话对象或错误消息。
	 * @throws Exception 人员绑定异常。
	 */
	private Object bindPerson(String phone, String openId) throws Exception {
		String phoneHash = this.signer.sign(phone);
		
		// 查询绑定信息
		boolean requireBound = true;
		Map<String, Object> bound = this.bcPersonDao.queryBoundInfo(openId);
		if (bound != null) {
			int boundState = (Integer)bound.get("boundstate");
			String boundPhone = (String)bound.get("boundphone");
			if (EntityState.VALID.equalsValue(boundState) || EntityState.FROZEN.equalsValue(boundState)) {
				if (!boundPhone.equals(phoneHash)) return "已绑定其他手机号";
				if (EntityState.FROZEN.equalsValue(boundState)) return "单位已冻结";
				requireBound = false;
			}
		}
		
		// 查询存在的人员
		Bigcustomerpersons person = this.bcPersonDao.queryExistsPersion(phoneHash);
		if (person == null) return "绑定的账号不存在";
		
		if (requireBound) {
			// 清除原有绑定
			if (bound != null) this.bcPersonDao.clearBound((String)bound.get("boundid"));
			
			// 绑定新账号
			person.setPersonweixin(openId);
		}
		
		// 修改最后登录时间
		person.setLastlogin(new Date());
		
		// 创建用户会话对象
		UserSession us = new UserSession();
		us.setUserid(person.getEntityid());
		us.setUsergroup(UserGroup.CUSTOMER.getValue());
		us.setUsername(person.getPersonname());
		us.setManager(person.getManagerflag());
		us.setPurchaser(person.getPurchaserflag());
		return us;
	}
	
	/**
	 * 绑定商户。
	 * @author Chrise 2019年6月5日
	 * @param phone 手机号。
	 * @param openId 开放标识。
	 * @return 用户会话对象或错误消息。
	 * @throws Exception 商户绑定异常。
	 */
	private Object bindMerchant(String phone, String openId) throws Exception {
		String phoneHash = this.signer.sign(phone);
		
		// 查询绑定信息
		boolean requireBound = true;
		Map<String, Object> bound = this.merchantsDao.queryBoundInfo(openId);
		if (bound != null) {
			int boundState = (Integer)bound.get("boundstate");
			String boundPhone = (String)bound.get("boundphone");
			if (EntityState.VALID.equalsValue(boundState) || EntityState.FROZEN.equalsValue(boundState)) {
				if (!boundPhone.equals(phoneHash)) return "已绑定其他手机号";
				if (EntityState.FROZEN.equalsValue(boundState)) return "商户已冻结";
				requireBound = false;
			}
		}
		
		// 查询存在的商户
		Merchants merchant = this.merchantsDao.queryExistsMerchant(phoneHash);
		if (merchant == null) return "绑定的账号不存在";
		
		if (requireBound) {
			// 清除原有绑定
			if (bound != null) this.merchantsDao.clearBound((String)bound.get("boundid"));
			
			// 绑定新账号
			merchant.setMerchantweixin(openId);
		}
		
		// 修改最后登录时间
		merchant.setLastlogin(new Date());
		
		// 创建用户会话对象
		UserSession us = new UserSession();
		us.setUserid(merchant.getEntityid());
		us.setUsergroup(UserGroup.MERCHANT.getValue());
		us.setUsername(merchant.getMerchantname());
		return us;
	}
}
